#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    ui->send_msg->setVisible(false);//0f和10功能组合帧发送按钮隐藏
    ui->coilsetepbtn->setVisible(false);//0f参数按钮隐藏
    ui->regsetupbtn->setVisible(false);//10参数按钮隐藏

    init();

    //信号连接
    connect(ui->GN_combox,SIGNAL(currentIndexChanged(QString)),this,SLOT(hide_SLOTS()));
}

//连接两个quint8数据为一个quint16数据
quint16 BondTwoUint8ToUint16(quint8 preNum, quint8 afterNum)
{
    quint16 bondNum = (preNum << 8) | afterNum;
    return bondNum;
}
//字节反转函数
void MainWindow::byteReverse(QString &coils)
{
    // 定义临时字符变量
    QChar temp;

    for(int i=0; i < 4; i++)
    {
        temp = coils[i];        // 将第i个元素存入临时字符变量
        coils[i] = coils[8-i-1];  // 将第i个字符元素和第n-i-1个元素对调
        coils[8-i-1] = temp;    // 将临时字符变量的值赋给第n-i-1个元素
    }
}

//字节数组转16进制字符串
void MainWindow::ByteToHexString(QString &str, QByteArray &ba)
{
    QString strs = ba.toHex().toUpper();//直接转换中间没有空格
    qDebug() << ba.toHex() << endl;

    for(int i=0;i<strs.length();i+=2)
    {
        QString str_1 = strs.mid (i,2);
        str += str_1;
        str += " ";
    }
}

QString HexByteArrayToHexString(QByteArray HexByteArr,int ConvertLen, int pattern = 0)
{
    //获得目标数组大小
    int HexByteArrSize = HexByteArr.size();
    //判断长度是否合法，如果长度大于数组长度，则设为数组长度，小于0则设置为0
    if(ConvertLen > HexByteArrSize)
    {
        ConvertLen = HexByteArrSize;
    }
    else if(ConvertLen < 0)
    {
        ConvertLen = 0;
    }

    //声明目标字符串
    QString readMes = NULL;

    for(int i = 0; i < ConvertLen; i++)
    {
        readMes += QString("%1").arg((quint8)HexByteArr.at(i),2,16,QLatin1Char('0'));
        //判断转换的模式
        if(pattern == 1)
        {
            readMes += " ";
        }
    }

    //返回转化后的十六进制字符串
    return readMes;
}

MainWindow::~MainWindow()
{
    delete ui;
}

//初始化
void MainWindow::init()
{
    //表格初始化
    table_dataInit();

    ui->reg_LineEdit->setVisible(false);
    ui->labelreg->setVisible(false);

    m_resendtimer = new QTimer(this);
    connect(m_resendtimer, SIGNAL(timeout()), this, SLOT(resendShow()));
    m_resendtimer->setInterval(3000);

    connect(this,&MainWindow::TCPAnalysisMessage,mb_msg,&MainWindow::TCPAnalysisMessage,
            Qt::QueuedConnection);
    connect(this,&MainWindow::packageSartc,mb_msg,&MainWindow::packageSartc,
            Qt::QueuedConnection);

    //限制从机地址
    connect(ui->slave_LineEdit,&QLineEdit::textChanged,[=](){
        if(ui->slave_LineEdit->text().toInt()>247||ui->slave_LineEdit->text().toInt()<1)
        {
            QMessageBox::warning(this,"提示","请检查从机地址是否合法！",QMessageBox::Ok,QMessageBox::NoButton);
            ui->slave_LineEdit->clear();
        }
    });

    //限制起时地址
    connect(ui->start_ad_LineEdit,&QLineEdit::textChanged,[=](){
        if(ui->start_ad_LineEdit->text().toInt()+ui->coil_LineEdit->text().toInt()>65536)
        {
            QMessageBox::warning(this,"提示","请检查起时地址和数量是否合法！",QMessageBox::Ok,QMessageBox::NoButton);
            ui->start_ad_LineEdit->clear();
        }
    });

    connect(ui->coil_LineEdit,&QLineEdit::textChanged,[=](){
        if(ui->start_ad_LineEdit->text().toInt()+ui->coil_LineEdit->text().toInt()>65536)
        {
            QMessageBox::warning(this,"提示","请检查起时地址和数量是否合法！",QMessageBox::Ok,QMessageBox::NoButton);
            ui->coil_LineEdit->clear();
        }
    });


    connect(ui->start_ad_LineEdit,&QLineEdit::textChanged,[=]{
       if(ui->start_ad_LineEdit->text().toInt()>65535)
       {
            QMessageBox::warning(this,"提示","请检查起时地址是否合法！",QMessageBox::Ok,QMessageBox::NoButton);
            ui->start_ad_LineEdit->clear();
       }
    });

        //限制线圈个数为2000
        connect(ui->coil_LineEdit,&QLineEdit::textChanged,[=](){
            if(ui->coil_LineEdit->text().toInt()>2000)
            {
                QMessageBox::warning(this,"提示","请检查线圈个数是否正确",QMessageBox::Ok,QMessageBox::NoButton);
                ui->coil_LineEdit->clear();
            }
        });


        if(ui->GN_combox->currentIndex()==1)
        {
        //限制线圈个数为2000
        connect(ui->reg_LineEdit,&QLineEdit::textChanged,[=](){
            if(ui->reg_LineEdit->text().toInt()>125)
            {
                QMessageBox::warning(this,"提示","请检查寄存器个数是否正确",QMessageBox::Ok,QMessageBox::NoButton);
                ui->reg_LineEdit->clear();
            }
        });
       }

    readseting = new QSettings("Data.ini", QSettings::IniFormat);
    //qDebug()<<"主线程:"<<QThread::currentThreadId();
    ReadOutIniData();
}
//表格初始化
void MainWindow::table_dataInit()
{
    //设置表格
    QStringList TableHeader;
    TableHeader << "地址" << "数据";
    ui->table_coil->setHorizontalHeaderLabels(TableHeader);
    ui->table_coil->setSelectionMode(QAbstractItemView::SingleSelection);
    ui->tableregs->setHorizontalHeaderLabels(TableHeader);
    ui->tableregs->setSelectionMode(QAbstractItemView::SingleSelection);
    ui->tableregs->setEditTriggers(QAbstractItemView::NoEditTriggers);
    ui->table_coil->setEditTriggers(QAbstractItemView::NoEditTriggers);
}

//将ini文件中的数据放到对应的表格中
void MainWindow::ReadOutIniData(void)
{
    for(int i = 0; i < (65535 + 1); i++)
    {
        //地址设置
        QString adr =  "0X" + QString("%1").arg(i,4,16,QLatin1Char('0'));
        ui->table_coil->setItem(0,i, new QTableWidgetItem(QString(adr).toUpper()));
        ui->table_coil->item(0,i)->setTextAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
        ui->tableregs->setItem(0,i, new QTableWidgetItem(QString(adr)));
        ui->tableregs->item(0,i)->setTextAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
        //读出线圈数据
        QString coilData = readseting->value("Section" + QString::number(i+1) + "/coil").toString();
        //读出寄存器数据
        QString registerData = readseting->value("Section" + QString::number(i+1) + "/regi").toString();
        //在线圈数据表中显示数据
        ui->table_coil->setItem(1,i,new QTableWidgetItem(coilData));
        //设置表格内文字水平+垂直对齐
        ui->table_coil->item(1,i)->setTextAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
        //在寄存器数据表中显示数据
        ui->tableregs->setItem(1,i,new QTableWidgetItem(registerData));
        //设置表格内文字水平+垂直对齐
        ui->tableregs->item(1,i)->setTextAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
    }
}

//功能码01 03隐藏写入值和字节数
void MainWindow::hide_SLOTS()
{
    //功能码为01 隐藏字节设置控件
    if(ui->GN_combox->currentIndex()==0)
    {

        ui->labelcoil->setVisible(true);
        ui->coil_LineEdit->setVisible(true);
        ui->labelstartaddrees->setVisible(true);
        ui->start_ad_LineEdit->setVisible(true);
        ui->labelslaveaddress->setVisible(true);
        ui->slave_LineEdit->setVisible(true);
        ui->labelcoil->setText("线圈个数:");
        ui->setupbtn->setVisible(true);
        ui->setupbtn->setText("发送");

        ui->send_msg->setVisible(false);//0f和10功能组合帧发送按钮隐藏
        ui->coilsetepbtn->setVisible(false);//0f参数按钮隐藏
        ui->regsetupbtn->setVisible(false);//10参数按钮隐藏
        ui->reg_LineEdit->setVisible(false);
        ui->labelreg->setVisible(false);

    }
    //功能码为03 隐藏字节设置控件
    else if (ui->GN_combox->currentIndex()==1)
    {
        ui->labelcoil->setVisible(true);
        ui->coil_LineEdit->setVisible(true);
        ui->labelstartaddrees->setVisible(true);
        ui->start_ad_LineEdit->setVisible(true);
        ui->labelslaveaddress->setVisible(true);
        ui->slave_LineEdit->setVisible(true);
        ui->labelcoil->setText("寄存器数量:");
        ui->setupbtn->setVisible(true);
         ui->setupbtn->setText("发送");
         ui->reg_LineEdit->setVisible(true);//寄存器输入框隐藏
         ui->labelreg->setVisible(true);//寄存器标签隐藏

         ui->send_msg->setVisible(false);
         ui->send_msg->setVisible(false);//0f和10功能组合帧发送按钮隐藏
         ui->coilsetepbtn->setVisible(false);//0f参数按钮隐藏
         ui->regsetupbtn->setVisible(false);//10参数按钮隐藏
         ui->labelcoil->setVisible(false);  //隐藏线圈
         ui->coil_LineEdit->setVisible(false);

         connect(ui->reg_LineEdit,&QLineEdit::textChanged,[=](){
             if(ui->reg_LineEdit->text().toInt()>125||ui->coil_LineEdit->text().toInt()<1)
             {
                 QMessageBox::warning(this,"提示","请检查寄存器个数是否正确",QMessageBox::Ok,QMessageBox::NoButton);
                 ui->reg_LineEdit->text().clear();
             }
         });
    }
    //功能码为10 显示字节设置控件
    else if (ui->GN_combox->currentIndex()==2)
    {
        //写入值输入框隐藏
        ui->coil_LineEdit->setVisible(false);
        ui->labelslaveaddress->setVisible(false);
        ui->labelstartaddrees->setVisible(false);
        ui->coilsetepbtn->setVisible(false);//0f参数按钮隐藏
        //线圈lable隐藏
        ui->labelcoil->setVisible(false);
        //从机地址输入框隐藏
        ui->slave_LineEdit->setVisible(false);
        //起时地址输入框隐藏
        ui->start_ad_LineEdit->setVisible(false);
        ui->setupbtn->setVisible(false);
        ui->send_msg->setVisible(true);
        ui->regsetupbtn->setVisible(true);//10参数按钮隐藏
        ui->reg_LineEdit->setVisible(false);
        ui->labelreg->setVisible(false);
    }
    //功能码为0f 显示字节设置控件
    else if (ui->GN_combox->currentIndex()==3)
    {
        //写入值输入框隐藏
        ui->coil_LineEdit->setVisible(false);
        ui->labelslaveaddress->setVisible(false);
        ui->labelstartaddrees->setVisible(false);
        //线圈lable隐藏
        ui->labelcoil->setVisible(false);
        //从机地址输入框隐藏
        ui->slave_LineEdit->setVisible(false);
        //起时地址输入框隐藏
        ui->start_ad_LineEdit->setVisible(false);
        ui->setupbtn->setText("设置参数");
        ui->send_msg->setVisible(true);

        ui->setupbtn->setVisible(false);
        ui->coilsetepbtn->setVisible(true);//0f参数按钮隐藏
        ui->regsetupbtn->setVisible(false);//10参数按钮隐藏
        ui->reg_LineEdit->setVisible(false);
        ui->labelreg->setVisible(false);
    }
}

//更新寄存器表
void MainWindow::wirtTabler(quint16 num, quint16 satrtaddr,QVector<quint16> bar)
{
    ui->tableregs->blockSignals(true);
    for(int i = satrtaddr,k=0; k < num;k++,i++)
    {
      ui->tableregs->setItem(i,1,new QTableWidgetItem(QString("%1").arg(bar[k])));
      ui->tableregs->item(i,1)->setTextAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
    }
    ui->tableregs->blockSignals(false);
    bar.clear();
}

//更新线圈表
void MainWindow::wirtTablec(quint16 num, quint16 satrtaddr,QString bac)
{
    ui->table_coil->blockSignals(true);
    for(int i = satrtaddr,k=0; k < num;k++,i++)
    {
        //在线圈数据表中显示数据
        QString data;
        if(bac[k] == '1')
        {
            data ="1";
        }
        else
        {
            data ="0";
        }
        ui->table_coil->setItem(i,1,new QTableWidgetItem(data));
        ui->table_coil->item(i,1)->setTextAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
    }
    ui->table_coil->blockSignals(false);
    bac.clear();
}

//将读取的线圈数据写入ini文件
void MainWindow::WirteCoilDataToIni(quint16 satrt, QString CoilData)
{
    //更新ini文件数据
    for(int j=0,k=satrt;j<CoilData.length();j++,k++)
    {
        QString s = "Section" + QString::number(k+1) + "/coil";
        quint8 coildata;
        if(CoilData.at(j)=='1')
        {
            coildata = 1;
        }
        else
        {
            coildata = 0;
        }
        readseting->setValue(s,coildata);
    }
    coildata.clear();
}

//将读取的寄存器数据写入ini文件
void MainWindow::WirteRegsDataToIni(quint16 startaddr, quint16 num,QVector<quint16> ba)
{
    for(quint16 i=0,k=startaddr;i<num;i++,k++)
    {
      //写入文件
      QString s = "Section" + QString::number(k+1) + "/regi";
      readseting->setValue(s,ba.at(i));
    }
}

//0f功能码参数设置
void MainWindow::on_coilsetepbtn_clicked()
{
    dialog0f = new Dialog0f(this);
    m_code = 0x0f;
    int flag = dialog0f->exec();
    if(!flag)
    {
        inf=dialog0f->infdata();
        coildata = dialog0f->getUserInputData();
    }
    delete dialog0f;
}

//10功能码参数设置
void MainWindow::on_regsetupbtn_clicked()
{
        dialog10 = new Dialog10(this);
        m_code = 0x10;
        int flag = dialog10->exec();
        if(!flag)
        {
            inf = dialog10->infdata();
            regdata = dialog10->getUserInputData();
        }
        delete dialog10;
}

//清除文本框槽函数
void MainWindow::on_clearbtn_clicked()
{
    ui->show_Edit->clear();
}

//发送消息槽
void MainWindow::m_SendMsg(QByteArray ba)
{
    sendflag = true;
    resenddata = ba;
    QString s;
    m_TcpChile->write(ba);
    QDateTime current_date_time =QDateTime::currentDateTime();
    QString current_date =current_date_time.toString("yyyy.MM.dd hh:mm:ss");
    ByteToHexString(s,ba);
    ui->show_Edit->append(current_date+"\n发送查询报文："+s+"\n");
    ui->show_Edit->moveCursor(QTextCursor::End);
    m_resendtimer->start();
}

//超时重发
void MainWindow::resendShow()
{
        resendNumber++;
        if(resendNumber<4)
        {
            QDateTime current_date_time =QDateTime::currentDateTime();
            QString current_date =current_date_time.toString("yyyy.MM.dd hh:mm:ss");
            ui->show_Edit->append(current_date+"从站无响应！");
            ui->show_Edit->append("第"
                                  + QString::number(resendNumber)
                                  + "次重新发送报文\n");
            m_TcpChile->write(resenddata);

        }
        else
        {
            resendNumber=0;
            ui->show_Edit->append("3次重传无效，从站无响应!\n");
            m_resendtimer->stop();
            if(resendNumber==0)
            {
                ui->setupbtn->setEnabled(true);
                ui->send_msg->setEnabled(true);
            }
        }
}

//读取消息槽
void MainWindow::m_ReadMsg()
{
    QByteArray ba;
    ba = m_TcpChile->readAll();
    if(sendflag)
    {
        if(ba.isEmpty()==true)
        {
            qDebug() <<"没有数据！";
           // m_resendtimer->start();
        }
        else
        {
            ui->setupbtn->setEnabled(true);
            ui->send_msg->setEnabled(true);
            m_resendtimer->stop();
            resendNumber=0;
            TCPAnalysisMessage(ba);
            sendflag =false;
        }
    }
    else
    {
       ba.clear();
    }
}
//解析报文
bool MainWindow::TCPAnalysisMessage(QByteArray MessageArray)
{
   // qDebug()<<"回应报文解析线程:"<<QThread::currentThreadId();
    bool analysisResult;
    /***************************************处理报文*************************************/
    //初始化报文字符串
    QString readMessage;
    //将十六进制数据转化成字符串
    readMessage = HexByteArrayToHexString(MessageArray, MessageArray.size(), 1);
    /***************************************显示报文*************************************/
    //显示响应报文
   showMsg("响应报文:\n"+readMessage+"\n");
    /***************************************解析报文*************************************/
    //报文合法性判断
    analysisResult = MessageLegalJudge(MessageArray, requestMessage);
    if(analysisResult == false)
    {
        return false;
    }

    //处理匹配的正常响应码
    switch(MessageArray.at(7))
    {
    case 1:
    {
        //0X01功能码报文处理函数
        analysisResult = analysis01(MessageArray, requestMessage);
        break;
    }

    case 3:
    {
        //0X03功能码报文处理函数
        analysisResult = analysis03(MessageArray, requestMessage);
        break;
    }

    case 15:
    {
        //0X0f功能码报文处理函数
        analysisResult = analysis0f(MessageArray, requestMessage);
        break;
    }

    case 16:
    {
        //0X10功能码报文处理函数
        analysisResult = analysis10(MessageArray, requestMessage);
        break;
    }
    }

    if(analysisResult == true)
    {
        //清空请求报文数组
        requestMessage.clear();
    }
    return analysisResult;
}

//解析部分-------------------------------------------------------------

//解析功能码01
bool MainWindow::analysis01(QByteArray MessageArr, QByteArray requestMessageArr)
{
    //处理匹配的正常响应码
    QString dataObtained;
    quint8 numOfByte;
    quint16 length;

    //判断响应报文的长度是否符合最低要求
    if(MessageArr.size() < 10)
    {
        //消息窗口显示信息
        showlogmsg("响应报文的长度异常！");
        return false;
    }

    //先判断响应报文的字节数是否和请求报文需要的对应
    //求出请求报文中想读的线圈数量number
    quint16 number = BondTwoUint8ToUint16((quint8)requestMessageArr.at(10),(quint8)requestMessageArr.at(11));

    //判断响应报文的字节数是否和满足number需要的字节数匹配
    //求出字节数
    numOfByte = (number + 7) / 8;

    //如果响应报文的字节数和满足number需要的字节数不匹配，则显示错误信息
    if(numOfByte != (quint8)MessageArr.at(8))
    {
        //消息窗口显示信息
        showlogmsg("响应报文的字节数异常！");
        return false;
    }

    //判断自身长度字段是否异常
    length =(quint16)BondTwoUint8ToUint16(MessageArr.at(4),MessageArr.at(5));
    //如果不匹配，则输出错误信息
    if(length != MessageArr.size() - 6)
    {
        //消息窗口显示信息
        showlogmsg("响应报文自身的长度字段异常！");
        return false;
    }

    //判断自身字节数字段是否异常
    if((quint8)MessageArr.at(8) != (MessageArr.size() - 9))
    {
        //消息窗口显示信息
        showlogmsg("响应报文自身的字节数字段异常！");
        return false;
    }

    //取出所读的多个线圈，并显示，数据从第九位开始
    for(int i = 0; i < (quint8)MessageArr.at(8); i++)
    {
        //先转化为2进制字符串
        QString str = QString::number((quint8)MessageArr.at(9 + i),2);
        //再转化为2进制整形，由二进制整形转化为8位2进制字符串前面自动补0，从而保证8位
        str = QString("%1").arg((quint8)str.toInt(NULL,2),8,2,QChar('0'));
        //8bit字节倒转
        byteReverse(str);
        //添加到数据中
        dataObtained += str;
        coildata +=str;
    }
    quint16 startaddr = BondTwoUint8ToUint16(requestMessageArr.at(8),requestMessageArr.at(9));
    quint16 num = BondTwoUint8ToUint16(requestMessageArr.at(10),requestMessageArr.at(11));
    //更新寄存器到表格
//    wirtTablec(num,startaddr,dataObtained);
    //去除填充的0位，读出请求报文请求的线圈数
    dataObtained = dataObtained.left(number);
    //显示起始地址 和数量
    ReadCoilPackMsgToShow(startaddr,num,MessageArr);
    showlogmsg(dataObtained);
    WirteCoilDataToIni(startaddr,coildata);
    return true;
}


void MainWindow::ReadCoilPackMsgToShow(quint16 startaddr,quint16 num,QByteArray msg)
{
    quint8 size = msg.size();
    QString msgs;
    for(quint8 i=0;i<size;i++)
    {
        //先转化为2进制字符串
        QString str = QString::number((quint8)msg.at(i),2);
        //再转化为2进制整形，由二进制整形转化为8位2进制字符串前面自动补0，从而保证8位
        str = QString("%1").arg((quint8)str.toInt(NULL,2),8,2,QChar('0'));
        //8bit字节倒转
        byteReverse(str);
        //添加到数据中
        msgs += str;
    }
    //去除填充的0位，读出请求报文请求的线圈数
    msgs = msgs.left(num);
    showlogmsg("成功读出线圈的数据\n");
    showlogmsg(QString("起始地址:%1").arg(startaddr)+"             "+QString("数量:%1\r\n").arg(num)+"\n");
    //showlogmsg(msgs+"\n");
    msgs.clear();
}

//0X03功能码报文处理函数
bool MainWindow::analysis03(QByteArray MessageArr, QByteArray requestMessageArr)
{
    //处理匹配的正常响应码
    QString dataObtained;
    quint16 length;

    //判断响应报文的长度是否符合最低要求
    if(MessageArr.size() < 10)
    {
        //消息窗口显示信息
        showlogmsg("响应报文的长度异常！");
        return false;
    }

    //先判断响应报文的字节数是否和请求报文需要的对应
    //求出请求报文中想读的寄存器数量number
    quint16 number = BondTwoUint8ToUint16((quint8)requestMessageArr.at(10),(quint8)requestMessageArr.at(11));

    //如果响应报文的字节数和满足number需要的字节数不匹配，则显示错误信息
    if(2*number != (quint8)MessageArr.at(8))
    {
        //消息窗口显示信息
         showlogmsg("响应报文的字节数异常！");
        return false;
    }

    //判断自身长度字段是否异常
    length =(quint16)BondTwoUint8ToUint16(MessageArr.at(4),MessageArr.at(5));
    //如果不匹配，则输出错误信息
    if(length != MessageArr.size() - 6)
    {
        //消息窗口显示信息
         showlogmsg("响应报文自身的长度字段异常！");
        return false;
    }

    //判断自身字节数字段是否异常
    if((quint8)MessageArr.at(8) != (MessageArr.size() - 9))
    {
        //消息窗口显示信息
        showlogmsg("响应报文自身的字节数字段异常！");
        return false;
    }
    quint16 startaddr = BondTwoUint8ToUint16(requestMessageArr.at(8),requestMessageArr.at(9));
    quint16 num = BondTwoUint8ToUint16(requestMessageArr.at(10),requestMessageArr.at(11));
    //取出所读的多个寄存器，并显示，数据从第9个字节开始
    for(int i = 0;i < (quint8)MessageArr.at(8); i += 2)
    {
        dataObtained += QString::number(BondTwoUint8ToUint16((quint8)MessageArr.at(9 + i),(quint8)MessageArr.at(10 + i)));
        bar.push_back((quint16)dataObtained.toInt());
        dataObtained += " ";
    }
    //更新寄存器到表格
//     wirtTabler(num,startaddr,bar);
    //消息窗口显示信息
     ReadRegsPackMsgToShow(startaddr,num,MessageArr);
     showlogmsg(dataObtained);
    WirteRegsDataToIni(startaddr,num,bar);
    return true;
}


//报文寄存器数据读出提示
void MainWindow::ReadRegsPackMsgToShow(quint16 startaddr,quint16 num,QByteArray msg)
{
    quint8 size = msg.size();
    QString msgs;
    quint16 data;
    for(quint8 i=0;i<size;i++)
    {
        QString str;
        data= msg.at(i);
        str = QString("%1 ").arg(data);
        msgs+=str;
    }
     showlogmsg("成功读出寄存器的数据\n");
     showlogmsg(QString("起始地址:%1").arg(startaddr)+"             "+QString("数量:%1\r\n").arg(num)+"\n");
    msgs.clear();
    msg.clear();
}

//0X0f功能码报文处理函数
bool MainWindow::analysis0f(QByteArray MessageArr, QByteArray requestMessageArr)
{
    //处理匹配的正常响应码
    quint16 messageArrayBeginAddress;
    quint16 requestMessageBegainAddress;
    quint16 length;
    quint16 messageArrayByteNum;
    quint16 requestMessageByteNum;

    //判断响应报文的长度是否符合最低要求，写入请求报文的响应报文为固定长度12
    if(MessageArr.size() != 12)
    {
        //消息窗口显示信息
        showlogmsg("响应报文的长度异常！");
        return false;
    }

    //判断自身长度字段是否异常
    length =(quint16)BondTwoUint8ToUint16(MessageArr.at(4),MessageArr.at(5));
    //如果不匹配，则输出错误信息
    if(length != 6)
    {
        //消息窗口显示信息
        showlogmsg("响应报文自身的长度字段异常！");
        return false;
    }

    //判断收到的报文的起始地址字段是否与请求报文匹配
    messageArrayBeginAddress = (quint16)BondTwoUint8ToUint16(MessageArr.at(8),MessageArr.at(9));
    requestMessageBegainAddress = (quint16)BondTwoUint8ToUint16(requestMessageArr.at(8),requestMessageArr.at(9));
    if(messageArrayBeginAddress != requestMessageBegainAddress)
    {
        //消息窗口显示信息
        showlogmsg("收到的报文的起始地址字段与请求报文不匹配！");
        return false;
    }

    //判断收到的报文的数量字段是否与请求报文匹配
    messageArrayByteNum = (quint16)BondTwoUint8ToUint16(MessageArr.at(10),MessageArr.at(11));
    requestMessageByteNum = (quint16)BondTwoUint8ToUint16(requestMessageArr.at(10),requestMessageArr.at(11));
    if(messageArrayByteNum != requestMessageByteNum)
    {
        //消息窗口显示信息
        showlogmsg("收到的报文的数量字段与请求报文不匹配！");
        return false;
    }
    //消息窗口显示信息
    WirteCoilPackMsgToShow(messageArrayBeginAddress,requestMessageByteNum,MessageArr);
    return true;

}

//报文线圈数据写入提示
void MainWindow::WirteCoilPackMsgToShow(quint16 startaddr,quint16 num,QByteArray msg)
{
    quint8 size = msg.size();
    QString msgs;
    for(quint8 i=0;i<size;i++)
    {
        //先转化为2进制字符串
        QString str = QString::number((quint8)msg.at(i),2);
        //再转化为2进制整形，由二进制整形转化为8位2进制字符串前面自动补0，从而保证8位
        str = QString("%1").arg((quint8)str.toInt(NULL,2),8,2,QChar('0'));
        //8bit字节倒转
        byteReverse(str);
        //添加到数据中
        msgs += str;
    }
    //去除填充的0位，读出请求报文请求的线圈数
    msgs = msgs.left(num);
    showlogmsg("成功写入线圈的数据\n");
    showlogmsg(QString("起始地址:%1").arg(startaddr)+"             "+QString("数量:%1\r\n").arg(num)+"\n");
    msgs.clear();
}

//0X10功能码报文处理函数
bool MainWindow::analysis10(QByteArray MessageArr, QByteArray requestMessageArr)
{
    //处理匹配的正常响应码
    quint16 messageArrayBeginAddress;
    quint16 requestMessageBegainAddress;
    quint16 length;
    quint16 messageArrayByteNum;
    quint16 requestMessageByteNum;

    //判断响应报文的长度是否符合最低要求，写入请求报文的响应报文为固定长度12
    if(MessageArr.size() != 12)
    {

        //消息窗口显示信息
        showlogmsg("响应报文的长度异常！");
        return false;
    }

    //判断自身长度字段是否异常
    length =(quint16)BondTwoUint8ToUint16(MessageArr.at(4),MessageArr.at(5));
    //如果不匹配，则输出错误信息
    if(length != 6)
    {
        //消息窗口显示信息
        showlogmsg("响应报文自身的长度字段异常！");
        return false;
    }

    //判断收到的报文的起始地址字段是否与请求报文匹配
    messageArrayBeginAddress = (quint16)BondTwoUint8ToUint16(MessageArr.at(8),MessageArr.at(9));
    requestMessageBegainAddress = (quint16)BondTwoUint8ToUint16(requestMessageArr.at(8),requestMessageArr.at(9));
    if(messageArrayBeginAddress != requestMessageBegainAddress)
    {
        //消息窗口显示信息
        showlogmsg("收到的报文的起始地址字段与请求报文不匹配！");
        return false;
    }

    //判断收到的报文的数量字段是否与请求报文匹配
    messageArrayByteNum = (quint16)BondTwoUint8ToUint16(MessageArr.at(10),MessageArr.at(11));
    requestMessageByteNum = (quint16)BondTwoUint8ToUint16(requestMessageArr.at(10),requestMessageArr.at(11));
    if(messageArrayByteNum != requestMessageByteNum)
    {
        //消息窗口显示信息
        showlogmsg("收到的报文的数量字段与请求报文不匹配！");

        return false;
    }

    //提示响应报文解析成功
    WirteRegsPackMsgToShow(messageArrayBeginAddress,messageArrayByteNum,MessageArr);
    return true;
}
//报文寄存器数据写入提示
void MainWindow::WirteRegsPackMsgToShow(quint16 startaddr,quint16 num,QByteArray msg)
{
    quint8 size = msg.size();
    QString msgs;
    quint16 data;
    for(quint8 i=0;i<size;i++)
    {
        QString str;
        data= (quint16)msg.at(i);
        str = QString("%1 ").arg(data);
        msgs+=str;
    }
     showlogmsg("成功写入寄存器的数据\n");
     showlogmsg(QString("起始地址:%1").arg(startaddr)+"             "+QString("数量:%1\r\n").arg(num)+"\n");
    msgs.clear();
}


//连接槽函数
void MainWindow::on_connectbtn_clicked()
{
    if(m_TcpChile==nullptr)
    {
        if(judgement("是否连接服务器"))
        {
            bool isok;
            qint16 port = ui->portlineEdit->text().toUShort(&isok);
            if(!isok)
            {
                QMessageBox::warning(this,"错误","远程端口设置错误，请重新输入");
                return;
            }
            QHostAddress ip(ui->iplineEdit->text());
            ip.toIPv4Address(&isok);
            if(!isok)
            {
                QMessageBox::warning(this,"错误","远程IP设置错误，请重新输入");
                return;
            }

            m_TcpChile=new QTcpSocket(this);
            m_TcpChile->connectToHost(ip,port);
            connect(m_TcpChile,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(ClientReadError(QAbstractSocket::SocketError)));
            //等待连接
            if (m_TcpChile->waitForConnected(500))
            {
                connect(m_TcpChile,SIGNAL(readyRead()),this,SLOT(m_ReadMsg()));

                //更新UI
                QDateTime current_date_time =QDateTime::currentDateTime();
                QString current_date =current_date_time.toString("yyyy.MM.dd hh:mm:ss ddd");
                ui->show_Edit->append(current_date+ "  连接服务器成功 "+QString("[%1：%2]\r\n").arg(ui->iplineEdit->text()).arg(ui->portlineEdit->text()));
                ui->connectbtn->setText("断开");
                tcpconnectflag = true;
            }
            else
            {
                QMessageBox::warning(this,"错误","连接超时或服务器未启动！");
                m_TcpChile->disconnect();
                m_TcpChile->abort();//终止
                m_TcpChile->deleteLater();
                m_TcpChile=nullptr;
                tcpconnectflag = false;
            }
        }
    }
    else
    {
        if(judgement("是否断开连接？"))
        {
            m_TcpChile->disconnect();//断开信号槽
            m_TcpChile->abort();//终止
            m_TcpChile->deleteLater();//释放
            m_TcpChile=nullptr;
            //更新UI
            QDateTime current_date_time =QDateTime::currentDateTime();
            QString current_date =current_date_time.toString("yyyy.MM.dd hh:mm:ss ddd");
            ui->show_Edit->append(current_date+ "  已断开服务器"+QString("[%1：%2]\r\n").arg(ui->iplineEdit->text()).arg(ui->portlineEdit->text()));
            ui->connectbtn->setText("连接");
            tcpconnectflag = false;
        }
    }
}

//错误信息
void MainWindow::ClientReadError(QAbstractSocket::SocketError)
{
    m_TcpChile->disconnect();//断开所有信号
    m_TcpChile->abort();//终止socket连接
    m_TcpChile->deleteLater();//释放
    m_TcpChile=nullptr;
    //更新UI
    QDateTime current_date_time =QDateTime::currentDateTime();
    QString current_date =current_date_time.toString("yyyy.MM.dd hh:mm:ss ddd");
    ui->show_Edit->append(current_date+ "  服务器断开 "+QString("%1：%2\r\n").arg(ui->iplineEdit->text()).arg(ui->portlineEdit->text()));
    ui->connectbtn->setText("连接");
}
//询问判断
bool MainWindow::judgement(QString msg)
{
    QMessageBox::StandardButton close;
    close = QMessageBox::question(this,"提示",msg,QMessageBox::Yes | QMessageBox::No,QMessageBox::No);

    if(close == QMessageBox::Yes)
    {
        return true;
    }
    else
    {
        return false;
    }
}

//二次检测地址，起始地址，数量用户输入数据是否为空
bool MainWindow::checkUserInputData()
{
    if(inf.isEmpty()||(inf.at(0)==0)||(inf.at(2)==0))
    {
        return false;
    }
    else
    {
        return true;
    }
}

//发送请求帧 0F和10
void MainWindow::on_send_msg_clicked()
{   
    if(m_TcpChile==nullptr)
    {
        QMessageBox::warning(this,"错误","请连接服务器!");
    }
    else
    {

        switch(m_code)
        {
            case 15:
            {
                if(!coildata.isEmpty())
                {
                    packageSartc(inf.at(0),15,inf.at(1),inf.at(2),coildata,regdata);
                    ui->send_msg->setEnabled(false);
                }
                else {
                    QMessageBox::warning(this,"错误","未配置线圈值！");
                }
                break;
            }
            case 16:
            {
                if(!regdata.isEmpty())
                {
                     packageSartc(inf.at(0),16,inf.at(1),inf.at(2),coildata,regdata);
                     ui->send_msg->setEnabled(false);
                }
                else {
                    QMessageBox::warning(this,"错误","未配置寄存器值！");                
                }
                break;
            }
        }
    }
}


//01和03发送
void MainWindow::on_setupbtn_clicked()
{

    if(m_TcpChile==nullptr)
    {
        QMessageBox::warning(this,"错误","请连接服务器!");
    }
    else {
        QByteArray data_01,data_03;
        //获取控件中整型数据
        quint8 str_slave = ui->slave_LineEdit->text().toInt();
        quint16 str_start = ui->start_ad_LineEdit->text().toInt();
        quint16 str_coil = ui->coil_LineEdit->text().toInt();
        quint16 str_reg = ui->reg_LineEdit->text().toInt();

        //选中功能码发送
        switch (ui->GN_combox->currentIndex())
        {
            //功能码01发送请求帧
            case 0:{
                        msgbuf[0] = mb_identifier>>8; // 事务标识符
                        msgbuf[1] = mb_identifier&0xFF;
                        msgbuf[2] = MB_PROTOCOL>>8; //高位
                        msgbuf[3] = MB_PROTOCOL&0xFF;//低位
                        msgbuf[4] = (mb_lenght+6)*256;
                        msgbuf[5] = (mb_lenght+6)&0xFF;
                        msgbuf[6] = str_slave;
                        msgbuf[7] = 0x01;
                        msgbuf[8] = str_start>>8;
                        msgbuf[9] = str_start&0xFF;
                        msgbuf[10] = str_coil>>8;
                        msgbuf[11] = str_coil&0xFF;
                        QByteArray ba((char*)msgbuf,12);
                        requestMessage = ba;
                        //emit package_over(ba);
                        memset(msgbuf,0,12);
                        if(str_slave==0||str_coil==0)
                        {
                            QMessageBox::warning(this,"错误","输入阈值中的数据");
                        }
                        else
                        {
                             m_SendMsg(ba);
                             ui->setupbtn->setEnabled(false);
                        }
                 break;
             }

             //功能码03发送请求帧
             case 1:
             {
                        msgbuf[0] = mb_identifier>>8;// 事务标识符
                        msgbuf[1] = mb_identifier&0xFF;
                        msgbuf[2] = MB_PROTOCOL>>8;//高位
                        msgbuf[3] = MB_PROTOCOL&0xFF;//低位
                        msgbuf[4] = (mb_lenght+6)>>8;
                        msgbuf[5] = (mb_lenght+6)&0xFF;
                        msgbuf[6] = str_slave;
                        msgbuf[7] = 0x03;
                        msgbuf[8] = str_start>>8;
                        msgbuf[9] = str_start&0xFF;
                        msgbuf[10] = str_reg>>8;
                        msgbuf[11] = str_reg&0xFF;
                        QByteArray ba((char*)msgbuf,12);
                        requestMessage = ba;
                        memset(msgbuf,0,12);
                        if(str_slave==0||str_reg==0)
                        {
                            QMessageBox::warning(this,"错误","输入阈值中的数据");
                        }
                        else
                        {
                            m_SendMsg(ba);
                            ui->setupbtn->setEnabled(false);
                        }
             }
                 break;
        default: break;
        }
    }
}

void MainWindow::packageSartc(quint8 addr,quint8 funcode,quint16 startaddr,quint16 num,QByteArray ba,QVector<quint16>regs)
{
    switch(funcode)
    {
    case  15:funCode0f(addr,startaddr,num,ba);
        break;
    case  16:funCode10(addr,startaddr,regs);
        break;
    }
}

//0f功能码报文封装函数
void MainWindow::funCode0f(quint8 addr,quint16 startaddr,quint16 num,QByteArray bas)
{
    //线圈字节计数
    quint8 sizenum=bas.size();
    //mb_identifier+=1;
    qDebug()<<mb_identifier;
    msgbuf[0] = mb_identifier>>8; // 事务标识符
    msgbuf[1] = mb_identifier&0xFF;
    msgbuf[2] = MB_PROTOCOL>>8; //高位
    msgbuf[3] = MB_PROTOCOL&0xFF;//低位
    msgbuf[4] =(7+sizenum)>>8;
    msgbuf[5] =(7+sizenum)&0xFF;
    msgbuf[6] = addr;
    msgbuf[7] = 0x0f;
    msgbuf[8] = startaddr>>8;
    msgbuf[9] = startaddr&0xFF;
    msgbuf[10] = num>>8;   //线圈数//高位
    msgbuf[11] = num&0xFF; //低位
    msgbuf[12] =(quint8)(sizenum) ;//字节计数
    for(quint16 i=0;i<sizenum;i++)
    {
        msgbuf[13+i] = (quint8)bas.at(i);
    }
    QByteArray ba((char*)msgbuf,(13+sizenum));
    requestMessage = ba;
     m_SendMsg(ba);
    memset(msgbuf,0,(13+sizenum));
    bas.clear();
}

//10功能码报文封装函数
void MainWindow::funCode10(quint8 addr,quint16 startaddr,QVector<quint16> addrvalue)
{
    //计算数据字节大小
    qDebug()<<mb_identifier;
    quint16 num= addrvalue.size();
    if(num>=122)
    {
        mb_identifier=1;
    }

    msgbuf[0] = mb_identifier>>8; // 事务标识符
    msgbuf[1] = mb_identifier&0xFF;
    msgbuf[2] = MB_PROTOCOL>>8; //高位
    msgbuf[3] = MB_PROTOCOL&0xFF;//低位
    msgbuf[4] =(7+num*2)>>8;
    msgbuf[5] =(7+num*2)&0xFF;
    msgbuf[6] = addr;
    msgbuf[7] = 0x10;
    msgbuf[8] = startaddr>>8;
    msgbuf[9] = startaddr&0xFF;
    msgbuf[10] = num>>8;
    msgbuf[11] = num&0xFF; //低位
    msgbuf[12] =(quint16)(num*2) ;//字节计数

    for(quint16 k=13,j=14,z=0;z<num;z++)
    {
        msgbuf[k++] = (quint16)addrvalue.at(z)>>8;
        msgbuf[k++] = (quint16)addrvalue.at(z)&0xFF;
    }
    QByteArray ba((char*)msgbuf,(13+num*2));
    requestMessage = ba;
    m_SendMsg(ba);
    memset(msgbuf,0,(13+num*2));
    addrvalue.clear();
}

//显示回应消息
void MainWindow::showMsg(QString msg)
{
    ui->show_Edit->append(msg);
}

//显示信息
void MainWindow::showlogmsg(QString msg)
{
    ui->show_Edit->append(msg);
}

// 报文合法性判断函数
bool MainWindow::MessageLegalJudge(QByteArray MessageArr, QByteArray requestMessageArr)
{
    bool Result;
    //1. 判断接收到的报文长度是否合法，合法最小长度为写入请求报文的异常响应报文，为9字节
    if(MessageArr.size() < 9)
    {
        //消息窗口显示信息
        showlogmsg("报文长度有误！");
        return false;
    }

    //2. 判断接收到的报文与请求报文的事务元标识符是否一致
    if((MessageArr.at(0) != requestMessageArr.at(0)) || (MessageArr.at(1) != requestMessageArr.at(1)))
    {
        //消息窗口显示信息
        showlogmsg("事务标识符不正确，收到的报文不是请求报文的响应报文！");
        return false;
    }

    //3. 判断接收到的报文的协议标识是否是Modbus协议
    if((MessageArr.at(2) != 0) || (MessageArr.at(3) != 0))
    {
        //消息窗口显示信息
        showlogmsg("协议标识符错误，收到的报文不是Modbus报文！");
        return false;
    }

    //4. 判断接收到的报文的长度的数据与其后的字节长度是否匹配
    if((MessageArr.size() - 6) != BondTwoUint8ToUint16(MessageArr.at(4),MessageArr.at(5)))
    {
        //消息窗口显示信息
        showlogmsg("长度字段出现错误！");
        return false;
    }

    //5. 判断接收到的报文的单元标识符是否与请求报文一致
    if(MessageArr.at(6) != requestMessageArr.at(6))
    {
        //消息窗口显示信息
        showlogmsg("不是请求的从站！");
        return false;
    }

    //6. 判断接收到的报文的功能码是否合法
    Result = FuncCodeLegalJudge(MessageArr);
    if(Result == false)
    {
        return false;
    }

    //7. 处理匹配的异常响应码信息
    Result = TCPExceptionCodeProcess(MessageArr);
    if(Result == false)
    {
        return false;
    }

    //8. 判断请求与响应的功能码是否一致
    if(MessageArr.at(7) != requestMessageArr.at(7))
    {
        //消息窗口显示信息
        showlogmsg("功能码不存在，收到报文的功能码与请求报文不一致！");
        return false;
    }

    return true;
}

// 功能码合法性判断函数
bool MainWindow::FuncCodeLegalJudge(QByteArray MessageArr)
{
    if(MessageArr.at(7) != 1
            &&MessageArr.at(7) != 3
            &&MessageArr.at(7) != 15
            &&MessageArr.at(7) != 16
            &&(quint8)MessageArr.at(7) != 0x81
            &&(quint8)MessageArr.at(7) != 0x83
            &&(quint8)MessageArr.at(7) != 0x8f
            &&(quint8)MessageArr.at(7) != 0x90)
    {
        //消息窗口显示信息
        showlogmsg("该报文的功能码无法识别！");
        return false;
    }
    else
    {
        return true;
    }
}
//异常码报文处理函数
bool MainWindow::TCPExceptionCodeProcess(QByteArray MessageArr)
{
    QString exceptionPrompt;
    //处理匹配的异常响应码信息
    switch ((quint8)MessageArr.at(7))
    {
    case 0x81:
    {
        exceptionPrompt = "读多个线圈出现异常！";
        break;
    }
    case 0x83:
    {
        exceptionPrompt = "读多个寄存器出现异常！";
        break;
    }
    case 0x8f:
    {
        exceptionPrompt = "写入多个线圈出现异常！";
        break;
    }
    case 0x90:
    {
        exceptionPrompt = "写入多个寄存器出现异常！";
        break;
    }
    default:
    {
        return true;
    }
    }

    //获取异常码
    quint8 exceptionCode = (quint8)MessageArr.at(8);

    showlogmsg(exceptionPrompt);

    //异常码判断
    TCPExceptionCodeJudge(exceptionCode);

    return false;
}
// 异常码判断函数
void MainWindow::TCPExceptionCodeJudge(quint8 excCode)
{
    QString exceptionCodePrompt;
    switch (excCode)
    {
    case 0x01:
    {
        exceptionCodePrompt = "异常码为：01 非法功能";
        break;
    }
    case 0x02:
    {
        exceptionCodePrompt = "异常码为：02 非法数据地址";
        break;
    }
    case 0x03:
    {
        exceptionCodePrompt = "异常码为：03 非法数据值";
        break;
    }
    }
    //消息窗口显示信息
    showlogmsg(exceptionCodePrompt);
    return;
}

//转到传输文件界面
void MainWindow::on_transfiles_clicked()
{
    widget = new Widget();
    widget->show();
}
//保存历史数据
void MainWindow::on_historyBtn_clicked()
{
    QString fileName=QFileDialog::getSaveFileName(this,"Save File",QDir::currentPath());
    if(fileName.isEmpty())
    {
        QMessageBox::information(this,"Error Message","请选择文件的路径");
        return;
    }
    QFile *file=new QFile;
    file->setFileName(fileName);
    bool ok=file->open(QIODevice::WriteOnly);
    if(ok)
    {
        QTextStream out(file);
        out<<ui->show_Edit->toPlainText();
        file->close();
        delete file;
    }
    else
    {
        QMessageBox::information(this,"Error Messaage","File Save Error"+file->errorString());
        return;
    }
}
